﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IGolovkine.Core.Linq;
using System.Threading;
using IGolovkine.Core.Diagnostics;

namespace IGolovkine.ShoutCastRipper.Service
{
    public interface IStationRipperInstanceManager : IDisposable
    {
        void AttachBackUp(IStationRipper backUpStationRipper);
    }

    public class StationRipperInstanceManager : IStationRipperInstanceManager
    {
        private readonly IStationRipper _primary;
        private readonly object _lock = new object();
        private readonly Dictionary<string, IStationRipper> _backUps;

        public StationRipperInstanceManager(IStationRipper primary)
        {
            _primary = primary;
            _primary.StatusChanged += new EventHandler<StationRipperStatusChangedEventArgs>(PrimaryStatusChanged);
            _backUps = new Dictionary<string, IStationRipper>();
        }

        public void AttachBackUp(IStationRipper backUp)
        {
            if (backUp == _primary)
            {
                throw new InvalidOperationException(string.Format(""));
            }

            if (_backUps.ContainsKey(backUp.Name))
            {
                throw new InvalidOperationException("");
            }
            backUp.StatusChanged += new EventHandler<StationRipperStatusChangedEventArgs>(BackUpStatusChanged);
            _backUps.Add(backUp.Name, backUp);
        }

        private void BackUpStatusChanged(object sender, StationRipperStatusChangedEventArgs e)
        {
            IStationRipper backUp = (IStationRipper) sender;
            if (e.RippingToDisconnected || e.SkippingToDisconnected)
            {
                this.GetBackUps(r => r.Status == StationRipperStatus.Stopped).ForEach(r => r.Start());
            }
            else if(e.DisconnectedToSkipping || e.DisconnectedToRipping)
            {
                if (this.GetBackUps(r => r.Status == StationRipperStatus.Ripping || r.Status == StationRipperStatus.Skipping).Count() > 0)
                {
                    backUp.Stop();
                }
            }
            else if(e.StoppedToRipping || e.StoppedToSkipping)
            {
                if (this.GetBackUps(r => r.Status == StationRipperStatus.Ripping || r.Status == StationRipperStatus.Skipping).Count() > 0)
                {
                    backUp.Stop();
                }
            }
        }

        private void PrimaryStatusChanged(object sender, StationRipperStatusChangedEventArgs e)
        {
            IStationRipper primary = (IStationRipper)sender;
            if (e.RippingToDisconnected || e.SkippingToDisconnected)
            {
                this.StartBackUps();
            }
            else if (e.DisconnectedToRipping || e.DisconnectedToSkipping)
            {
                if (this.IsBackUpRippingInProgress())
                {
                    primary.SkipCurrentTitle("Back up instance ripping in progress");
                }
                this.StopBackUps();
            }
            else if (e.CurrentValue == StationRipperStatus.Stopped)
            {
                this.GetBackUps(r => r.Status != StationRipperStatus.Stopped).ForEach(r => r.Stop());
            }
        }

        private bool IsBackUpRippingInProgress()
        {
            IEnumerable<IStationRipper> rippingBackUps = this.GetBackUps(r=>r.Status == StationRipperStatus.Ripping);
            Debug.Expect(() => rippingBackUps.Count() <= 1);
            return rippingBackUps.Count() > 0;
        }

        private void StopBackUps()
        {
            this.GetBackUps(r => r.Status == StationRipperStatus.Ripping).ForEach(r => r.StopAfterCurrentTitle());
            this.GetBackUps(r => r.Status == StationRipperStatus.Disconnected).ForEach(r => r.Stop());
            this.GetBackUps(r => r.Status == StationRipperStatus.Skipping).ForEach(r => r.Stop());
        }

        private void StartBackUps()
        {
            foreach (IStationRipper backUp in this.GetBackUps(r=>r.IsStoppingAfterCurrentTitle))
            {
                backUp.CancelStopAfterCurrentTitle();
                return;
            }

            this.GetBackUps(r => r.Status == StationRipperStatus.Stopped).ForEach(r => r.Start());
        }

        private IEnumerable<IStationRipper> GetBackUps(Func<IStationRipper, bool> condition)
        {
            return _backUps.Values.Where(condition);
        }

        public void Dispose()
        {
            _primary.Dispose();
            _backUps.Values.ForEach(r => r.Dispose());
        }
    }
}
